package parsingmodbus

import (
	"encoding/binary"
	"errors"
	"fmt"
)

type ModbusMessage struct {
	Mode         string   //模式：RTU TCP
	RequestData  []byte   //请求数据
	ResponseData []byte   //响应数据
	Request      Request  //请求
	Response     Response //响应
	ByteOrder    string   //字节序 big little
}
type Request struct {
	SlaveId  uint8  //从机地址
	Function uint8  //功能码
	Address  uint16 //起始地址
	Quantity uint16 //数量
	Data     []byte //数据
}
type Response struct {
	SlaveId  uint8  //从机地址
	Function uint8  //功能码
	Data     []byte //数据
}

func (m *ModbusMessage) NewModbusMessage(mode string, byteOredr string) *ModbusMessage {
	m.Mode = mode
	m.ByteOrder = byteOredr
	return &ModbusMessage{}
}

// 放入请求数据
func (m *ModbusMessage) SetRequestData(data []byte) error {
	m.RequestData = data
	// 解析请求数据
	var err error
	m.Request, err = m.ParseRequestData(data)
	if err != nil {
		return err
	}
	return nil
}

// 解析请求数据
func (m *ModbusMessage) ParseRequestData(data []byte) (Request, error) {
	var request Request
	if m.Mode == "RTU" {
		// 检查数据长度
		if len(data) < 8 {
			return request, fmt.Errorf("data length too short for RTU mode")
		}
		request.SlaveId = data[0]
		request.Function = data[1]
		request.Address = binary.BigEndian.Uint16(data[2:4])
		request.Quantity = binary.BigEndian.Uint16(data[4:6])
		request.Data = data[6 : len(data)-2]
		// TODO: 检查CRC
		err := m.checkCRC(data)
		if err != nil {
			return request, err
		}
	} else if m.Mode == "TCP" {
		// 检查数据长度
		if len(data) < 12 {
			return request, fmt.Errorf("data length too short for TCP mode")
		}
		// TODO: 检查MBAP头部
		err := checkMBAPHeader(data, m.ByteOrder)
		if err != nil {
			return request, err
		}
		request.SlaveId = data[6]
		request.Function = data[7]
		request.Address = binary.BigEndian.Uint16(data[8:10])
		request.Quantity = binary.BigEndian.Uint16(data[10:12])
		request.Data = data[12:]
	}
	return request, nil
}

// 放入响应数据
func (m *ModbusMessage) SetResponseData(data []byte) error {
	m.ResponseData = data
	// 解析响应数据
	var err error
	m.Response, err = m.ParseResponseData(data)
	if err != nil {
		return err
	}
	return nil
}

// 解析响应数据
func (m *ModbusMessage) ParseResponseData(data []byte) (Response, error) {
	var response Response
	if m.Mode == "RTU" {
		// 检查数据长度
		if len(data) < 5 {
			return response, fmt.Errorf("data length too short for RTU mode")
		}
		// TODO: 检查CRC
		err := m.checkCRC(data)
		if err != nil {
			return response, err
		}
		response.SlaveId = data[0]
		response.Function = data[1]
		response.Data = data[3 : len(data)-2]

	} else if m.Mode == "TCP" {
		// 检查数据长度
		if len(data) < 9 {
			return response, fmt.Errorf("data length too short for TCP mode")
		}
		// TODO: 检查MBAP头部
		err := checkMBAPHeader(data, m.ByteOrder)
		if err != nil {
			return response, err
		}
		response.SlaveId = data[6]
		response.Function = data[7]
		response.Data = data[9:]

	}
	return response, nil
}

func checkMBAPHeader(data []byte, byteOrder string) error {
	if len(data) < 7 {
		return errors.New("data length too short")
	}
	var transactionIdentifier uint16
	var protocolIdentifier uint16
	var length uint16
	if byteOrder == "big" {
		transactionIdentifier = binary.BigEndian.Uint16(data[0:2])
		protocolIdentifier = binary.BigEndian.Uint16(data[2:4])
		length = binary.BigEndian.Uint16(data[4:6])
	} else {
		transactionIdentifier = binary.LittleEndian.Uint16(data[0:2])
		protocolIdentifier = binary.LittleEndian.Uint16(data[2:4])
		length = binary.LittleEndian.Uint16(data[4:6])
	}
	unitIdentifier := data[6]

	fmt.Printf("Transaction Identifier: %d\n", transactionIdentifier)
	fmt.Printf("Protocol Identifier: %d\n", protocolIdentifier)
	fmt.Printf("Length: %d\n", length)
	fmt.Printf("Unit Identifier: %d\n", unitIdentifier)

	if protocolIdentifier != 0 {
		return errors.New("invalid protocol identifier")
	}
	if length != uint16(len(data)-6) {
		return errors.New("invalid length")
	}
	return nil
}

// checkCRC 检查Modbus RTU报文的CRC
func (m *ModbusMessage) checkCRC(data []byte) error {
	if len(data) < 3 { // 最短的Modbus RTU报文应包含至少1字节的地址、1字节的功能码和2字节的CRC
		return errors.New("data too short for Modbus RTU message")
	}
	fmt.Println("data", data)
	message := data[:len(data)-2]
	fmt.Println("message", message)
	var crcInMessage uint16
	if m.ByteOrder == "big" {
		crcInMessage = binary.BigEndian.Uint16(data[len(data)-2:])
	} else {
		crcInMessage = binary.LittleEndian.Uint16(data[len(data)-2:])
	}
	fmt.Println("crcInMessage", crcInMessage)
	calculatedCRC := m.calculateCRC(message)

	if crcInMessage != calculatedCRC {
		return fmt.Errorf("CRC mismatch: calculated %04X, in message %04X", calculatedCRC, crcInMessage)
	}
	return nil
}

// 计算Modbus RTU CRC
func (m *ModbusMessage) calculateCRC(data []byte) uint16 {
	var crc uint16 = 0xFFFF
	for _, v := range data {
		crc ^= uint16(v)
		for i := 0; i < 8; i++ {
			if (crc & 0x0001) != 0 {
				crc >>= 1
				crc ^= 0xA001
			} else {
				crc >>= 1
			}
		}
	}
	return crc
}
